//c
#include <unistd.h>
#include <string.h>

//stream
#include <iostream>
#include <sstream>
#include <fstream>

//containers
#include <vector>
#include <queue>
#include <stack>
#include <list>
#include <array>
#include <bitset>
#include <unordered_map>
#include <unordered_set>
#include <map>
#include <set>
#include <string>

//others
#include <algorithm>
#include <numeric>
#include <bitset>
#include <random>

using namespace std;
class Solution{
private:
	//此函数为求区间和写法
	vector<int> raw;
	int t[50002 << 2];        //a为原来区间，t为线段树，t的大小是a的四倍, 用来防止下标溢出RE
	int lazy[50002 << 2];              //懒惰标记, 的大小是a的四倍, 用来防止下标溢出RE
	int rIndex;  //最大下标

	void Pushup(int k) {        //更新函数，这里是实现最大值 ，同理可以变成，最小值，区间和等
		t[k] = t[k << 1] + t[k << 1 | 1];
	}

	//递归方式建树 build(1,1,n);
	//此函数仅在main调用一次, 线段树的结构在pushup过程中不变
	void build(int k, int l, int r) {    //k为当前需要建立的结点，l为当前需要建立区间的左端点，r则为右端点
		if (l == r)    //左端点等于右端点，即为叶子节点，直接赋值即可
			t[k] = raw[l];
		else {
			int m = l + ((r - l) >> 1);    //m则为中间点，左儿子的结点区间为[l,m],右儿子的结点区间为[m+1,r]
			build(k << 1, l, m);    //递归构造左儿子结点
			build(k << 1 | 1, m + 1, r);    //递归构造右儿子结点
			Pushup(k);    //更新父节点
		}
	}

	void Pushdown(int k) {    //更新子树的lazy值，这里是RMQ的函数，要实现区间和等则需要修改函数内容
		if (lazy[k]) {    //如果有lazy标记
			lazy[k << 1] += lazy[k];    //更新左子树的lazy值
			lazy[k << 1 | 1] += lazy[k];    //更新右子树的lazy值
			t[k << 1] += lazy[k];        //左子树的最值加上lazy值
			t[k << 1 | 1] += lazy[k];    //右子树的最值加上lazy值
			lazy[k] = 0;    //lazy值归0
		}
	}

	//递归更新区间 updata(L,R,v,1,n,1);
	//v是待增加的值, 可能是个负的!
	void update(int L, int R, int v, int l, int r, int k) {    //[L,R]即为要更新的区间，l，r为结点区间，k为结点下标
		if (L <= l && r <= R) {    //如果当前结点的区间真包含于要更新的区间内
			lazy[k] += v;    //懒惰标记
			t[k] += v;    //最大值加上v之后，此区间的最大值也肯定是加v
		}
		else {
			Pushdown(k);    //重难点，查询lazy标记，更新子树
			int m = l + ((r - l) >> 1);
			if (L <= m)    //如果左子树和需要更新的区间交集非空
				update(L, R, v, l, m, k << 1);
			if (m < R)    //如果右子树和需要更新的区间交集非空
				update(L, R, v, m + 1, r, k << 1 | 1);
			Pushup(k);    //更新父节点
		}
	}

	//递归方式区间查询 query(L,R,1,n,1);
	int query(int L, int R, int l, int r, int k) {    //[L,R]即为要查询的区间，l，r为结点区间，k为结点下标
		if (L <= l && r <= R)    //如果当前结点的区间真包含于要查询的区间内，则返回结点信息且不需要往下递归
			return t[k];
		else {
			Pushdown(k);    /**每次都需要更新子树的Lazy标记*/
			int res = 0;    //返回值变量，根据具体线段树查询的什么而自定义
			int m = l + ((r - l) >> 1);    //m则为中间点，左儿子的结点区间为[l,m],右儿子的结点区间为[m+1,r]
			if (L <= m)    //如果左子树和需要查询的区间交集非空
				res += query(L, R, l, m, k << 1);
			if (R > m)    //如果右子树和需要查询的区间交集非空，注意这里不是else if，因为查询区间可能同时和左右区间都有交集
				res += query(L, R, m + 1, r, k << 1 | 1);

			return res;    //返回当前结点得到的信息
		}
	}
public:
    Solution(vector<int>& initVector){
		int nSize = initVector.size();
		if (nSize == 0) return;
		raw = initVector;
		raw.insert(raw.begin(), -99999);  //相当于移动下标
		memset(t, 0, (50002 << 2) * sizeof(int));
		memset(lazy, 0, (50002 << 2) * sizeof(int));

		rIndex = initVector.size();
		this->build(1, 1, rIndex);
    }

	void update(int index, int value){
        int old = this->query(index + 1, index + 1, 1, rIndex, 1);
		this->update(index + 1, index + 1, value - old, 1, rIndex, 1);
	}

    int sum(int startIndex, int endIndex){
		return this->query(startIndex + 1, endIndex + 1, 1, rIndex, 1);
/*
        int ret = 0;
        for (int i = startIndex; i <= endIndex; ++i){
            ret += mem[i];
        }
        return ret;
        */
    }
};
#include "mGenJson.hpp"
#include "template_fillInputArgs.hpp"

static const string inputfilePos = "input.txt";                 //for custom input thing read
static const string outputfilePos = "output.txt";

static const string inputSubmitfilePos = "inputSubmit.txt";     //for miltiple testcases judge
static const string outputSubmitfilePos = "outputSubmit.txt";   //for miltiple testcases judge

/*
 * read data from file, 
 * input const std::string& fileName
 * return pair<bool, vector<string>>, if OK, first true, second filecontent; else first false, second ""
 */
pair<bool, vector<string>> read_file_to_string(std::string const& fileName)
{
	std::ifstream fin(fileName, std::ios::in);

    if (!fin.is_open()) {
		cout << "fileName:" << fileName << " not found" << endl;
        return {false, {}};
    } else {
		vector<string> res;
		char line[524288] = {0};
		while(fin.getline(line, sizeof(line)))
		{
			string temp = (string)line;
			if (!temp.empty() && temp.back() == 0x0D){
				temp.pop_back();
			}
			res.emplace_back(temp);
		}
		fin.clear();
		fin.close();
		return {true, res};
    }
}

/*
 * create file
 * input string const& fileName,
 * return bool, if OK, true; else false
 */
bool create_file_truncate(string const& fileName){
    std::fstream s;
	s.open(fileName, std::fstream::out|std::fstream::trunc);
    if (!s.is_open()) {
        return false;
    } else {
		s.close();
		return true;
    }
}

/*
 * write data to file
 * input string const& fileName, string const& content
 * return bool, if OK, true; else false
 */
bool write_string_to_file_append(string const& fileName, string const& content)
{
    std::fstream s;
	s.open(fileName, std::fstream::out|std::fstream::app);
    if (!s.is_open()) {
        return false;
    } else {
		s << content;
		s.close();
		return true;
    }
}

/*
 * note: must input with exactedly two para, first shall be a timestamp type
 *    second must be a name with full directory, eq: testproblems/sort/
 * return code
 *        0x30: OK; 
 *        0x31: para error;
 *        0x32: input file not occured;
 *        0x33: input data not valid/inputSubmit.txt or outputSubmit.txt file not valid or their lines number not matched; 
 *        0x34: output file cannot saved;,    
 *        1: ADDRESS_SANITIZER_ERROR,         directly catched by cppJudgeSystem parent!
 *        0x35: wrong answer
 *        0x36: debug
 *        the other codes are not predifined, if have other normal exit with exitcode(program normal exit by inside fuction runtime error!, if occur, tell me), in common condition, YOU SHOULD NOT use these not predifined codes here, otherwise confusing question may occur!
 */
int main(int argc, char* argv[]) {
	const int howManyLineOfInputOneCase = 2;


	if (argc != 3){
		return 0x31;
	}else{
		string testId(argv[1]);
		
		string directory = argv[2];
		
		string tempOut;
#ifndef submit
		//read data from input.txt, so many data
		pair<bool, vector<string>> readret = read_file_to_string(directory + testId + inputfilePos);
		if (!readret.first){
			return 0x32;
		}
		
		//clear openfile, write input first
		if (!create_file_truncate(directory + testId + outputfilePos)){
			return 0x34;
		}
		if (!write_string_to_file_append(directory + testId + outputfilePos,
		    readret.second[0]
			)){
			return 0x34;
		}
		
		//use JSONlib to decode fileret.second!
		std::vector<int> total;
		std::vector<int> paras;
		pair<bool, string> ret = m_JSONFrame::fillInputArgs(readret.second, 0, &total, &paras);

		if (!ret.first) return 0x33;  //not valid, how to return the warning datas should be updated in the next version
		
		Solution s = Solution(total);
		
        std::vector<int> out;
		for (int j = 0; j < paras.size(); j += 3) {
			int op = paras[j];
			if (op == 1) {
				int index = paras[j + 1];
				int newV = paras[j + 2];
				s.update(index, newV);
			}
			else if (op == 2) {
				int stIndex = paras[j + 1];
				int enIndex = paras[j + 2];
				if (stIndex > enIndex) {
					return 0x33;
				}
				int ret = s.sum(stIndex, enIndex);
                out.emplace_back(ret);
			}
			else {
				return 0x33;
			}
		}

		//may use JSONlib to encode res!
		mGenJson m = mGenJson(out);
		string JSONres = m.getJSONString();
		
		//write output second
		tempOut += "\ncustomtest&" + JSONres + "&no compare res:(, not implemented yet";
		if (!write_string_to_file_append(directory + testId + outputfilePos,
		    tempOut
			)){  //note: output data split with #, for analyse of client!
			return 0x34;
		}
		
		return 0x30;
#else
		//clear openfile, dump write, for purpose of create empty file; if inputSubmit or outputSubmit txt format error, 
		//    the (cannot read output.txt after execv) error will not be thrown from the caller of cpp judgesystem:)
		if (!create_file_truncate(directory + testId + outputfilePos)){
			return 0x34;
		}

		//read data from inputSubmit/outputSubmit.txt
		pair<bool, vector<string>> readret = read_file_to_string(directory + inputSubmitfilePos);
		pair<bool, vector<string>> oureadret = read_file_to_string(directory + outputSubmitfilePos);
		if (!readret.first || !oureadret.first || (readret.second.size() != oureadret.second.size()*howManyLineOfInputOneCase)){
			return 0x32;
		}
		
		for (int line = 0; line < oureadret.second.size(); line++){
			//clear openfile, write input first
			if (!create_file_truncate(directory + testId + outputfilePos)){
				return 0x34;
			}
			if (!write_string_to_file_append(directory + testId + outputfilePos,
				readret.second[howManyLineOfInputOneCase*line + 0] + "&&&" + readret.second[howManyLineOfInputOneCase*line + 1]
				)){
				return 0x34;
			}
			
			//use JSONlib to decode fileret.second!
			std::vector<int> total;
			std::vector<int> paras;
			pair<bool, string> ret = m_JSONFrame::fillInputArgs(readret.second, howManyLineOfInputOneCase*line + 0, &total, &paras);

			if (!ret.first) return 0x33;  //not valid, how to return the warning datas should be updated in the next version
			
			Solution s = Solution(total);
			
			std::vector<int> out;
			for (int j = 0; j < paras.size(); j += 3) {
				int op = paras[j];
				if (op == 1) {
					int index = paras[j + 1];
					int newV = paras[j + 2];
					s.update(index, newV);
				}
				else if (op == 2) {
					int stIndex = paras[j + 1];
					int enIndex = paras[j + 2];
					if (stIndex > enIndex) {
						return 0x33;
					}
					int ret = s.sum(stIndex, enIndex);
					out.emplace_back(ret);
				}
				else {
					return 0x33;
				}
			}

			//may use JSONlib to encode res!
			mGenJson m = mGenJson(out);
			string JSONres = m.getJSONString();
			
			if (JSONres != oureadret.second[line]){
				//save output to XXXXoutput.txt
				tempOut += "\ntestcase" + to_string(line) + "&" + JSONres + "&" + oureadret.second[line];
				if (!write_string_to_file_append(directory + testId + outputfilePos,
					tempOut
					)){  //note: output data split with &, for analyse of client!
					return 0x34;
				}
				return 0x35;
			}
		}
		if (!create_file_truncate(directory + testId + outputfilePos)){
			return 0x34;
		}
		tempOut += "AC\npassed all" + to_string(oureadret.second.size()) + " testcases &AC&AC";
		if (!write_string_to_file_append(directory + testId + outputfilePos,
			tempOut
			)){  //note: output data split with &, for analyse of client!
			return 0x34;
		}
		return 0x30;
#endif
	}
}
